package com.stars.distributed.schedule.handler.mvchandler;

import com.stars.distributed.schedule.bean.*;
import com.stars.distributed.schedule.constant.DistributedScheduleConstants;
import com.stars.distributed.schedule.core.DistributedScheduleManager;
import com.stars.distributed.schedule.enums.*;
import com.stars.distributed.schedule.service.DistributedScheduleService;
import com.stars.distributed.schedule.util.*;
import org.springframework.web.bind.annotation.PathVariable;

import javax.servlet.http.HttpServletRequest;
import java.sql.Timestamp;
import java.util.*;

/**
 * 任务处理类
 *
 * @author guoguifang
 */
public class DbScheduleTaskMvcHandler extends BaseDbScheduleMvcHandler {

    @DbSchedulePost("addTask")
    public DataMsg addTask(HttpServletRequest request) {
        DataMsg dataMsg = DataMsg.getFailDataMsg();
        DbScheduleTask dbScheduleTask = new DbScheduleTask();

        // 校验taskId是否为空以及是否已存在
        String taskId = getParameterValue(request, "taskId");
        if (taskId == null) {
            dataMsg.setMsgCode("taskIdNull");
            return dataMsg;
        }
        if (DistributedScheduleService.getInstance().checkExistByTaskId(taskId) > 0) {
            dataMsg.setMsgCode("taskIdExist");
            return dataMsg;
        }
        dbScheduleTask.setTaskId(taskId);

        // 校验任务类型是否输入正确
        String taskTypeStr = getParameterValue(request, "taskType", TaskType.NATIVE.getCode());
        TaskType taskType = TaskType.forCode(taskTypeStr);
        if (TaskType.NATIVE == taskType) {
            String beanId = getParameterValue(request, "beanId");
            String beanClass = getParameterValue(request, "beanClass");
            if (beanId == null && beanClass == null) {
                dataMsg.setMsgCode("beanIsNull");
                return dataMsg;
            }
            dbScheduleTask.setBeanId(beanId);
            dbScheduleTask.setBeanClass(beanClass);
        } else if (TaskType.REMOTE == taskType) {
            String remoteUrl = getParameterValue(request, "remoteUrl");
            if (remoteUrl == null) {
                dataMsg.setMsgCode("remoteUrlIsNull");
                return dataMsg;
            }
            dbScheduleTask.setRemoteUrl(remoteUrl);
        } else if (TaskType.MQ == taskType) {
            String mqConfigId = getParameterValue(request, "mqConfigId");
            String mqDestination = getParameterValue(request, "mqDestination");
            String mqCallback = getParameterValue(request, "mqCallback");
            if (mqConfigId == null || mqDestination == null || mqCallback == null) {
                dataMsg.setMsgCode("mqIsNull");
                return dataMsg;
            }
            dbScheduleTask.setMqConfigId(mqConfigId);
            dbScheduleTask.setMqDestination(mqDestination);
            dbScheduleTask.setMqCallback(mqCallback);
        } else {
            dataMsg.setMsgCode("taskTypeError");
            return dataMsg;
        }
        dbScheduleTask.setTaskType(taskTypeStr);

        // 校验分区数量
        String partitionCountStr = getParameterValue(request, "partitionCount");
        if (partitionCountStr == null || !StringUtils.isNumeric(partitionCountStr)) {
            dataMsg.setMsgCode("partitionCountError");
            return dataMsg;
        }
        dbScheduleTask.setPartitionCount(Integer.parseInt(partitionCountStr) == 0 ? 1 : Integer.parseInt(partitionCountStr));

        // 校验周期表达式
        String cronExpression = getParameterValue(request, "cronExpression");
        if (cronExpression != null) {
            for (String cronExpressionStr : cronExpression.split(";")) {
                if (!CronExpression.isValidExpression(cronExpressionStr.trim())) {
                    dataMsg.setMsgCode("cronExpressionError");
                    return dataMsg;
                }
            }
            dbScheduleTask.setCronExpression(cronExpression);
        }

        // 校验下次执行时间
        String nextFireTimeStr = getParameterValue(request, "nextFireTime");
        Timestamp nextFireTime;
        if (nextFireTimeStr == null) {
            if (cronExpression != null) {
                Date nextFireDate = CronExpressionUtil.getNextFireDate(cronExpression, new Date());
                if (nextFireDate == null) {
                    dataMsg.setMsgCode("cronExpressionError");
                    return dataMsg;
                }
                nextFireTime = new Timestamp(nextFireDate.getTime());
            } else {
                nextFireTime = new Timestamp(System.currentTimeMillis());
            }
        } else {
            try {
                nextFireTime = Timestamp.valueOf(nextFireTimeStr);
            } catch (Exception e) {
                dataMsg.setMsgCode("nextFireTimeError");
                return dataMsg;
            }
        }
        dbScheduleTask.setNextFireTime(nextFireTime);

        // 校验失败策略
        String errorPolicy = getParameterValue(request, "errorPolicy", ErrorPolicy.RETRY.getCode());
        if (ErrorPolicy.RETRY == ErrorPolicy.forCode(errorPolicy)) {
            String retryCountStr = getParameterValue(request, "retryCount");
            if (retryCountStr == null) {
                retryCountStr = "5";
            } else if (!StringUtils.isNumeric(retryCountStr)) {
                dataMsg.setMsgCode("retryCountError");
                return dataMsg;
            }
            dbScheduleTask.setRetryCount(Integer.parseInt(retryCountStr) == 0 ? 1 : Integer.parseInt(retryCountStr));
            dbScheduleTask.setRetryIndex(0);
            String retryInterval = getParameterValue(request, "retryInterval");
            if (retryInterval == null) {
                retryInterval = "1m";
            } else if (TimeUtil.parseToMilliseconds(retryInterval) == null) {
                dataMsg.setMsgCode("retryIntervalError");
                return dataMsg;
            }
            dbScheduleTask.setRetryInterval(retryInterval);
            dbScheduleTask.setRetryPolicy(getParameterValue(request, "retryPolicy", RetryPolicy.GRADUAL.getCode()));
        } else if (ErrorPolicy.MANUAL != ErrorPolicy.forCode(errorPolicy) && ErrorPolicy.IGNORE != ErrorPolicy.forCode(errorPolicy)) {
            dataMsg.setMsgCode("errorPolicyError");
            return dataMsg;
        }
        dbScheduleTask.setErrorPolicy(errorPolicy);

        // 校验任务参数
        String parameters = getParameterValue(request, "parameters");
        if (parameters != null && !MapUtil.check(parameters)) {
            dataMsg.setMsgCode("parametersError");
            return dataMsg;
        }
        dbScheduleTask.setParameters(parameters);

        Long id = GenerateUtil.getTaskId();
        String useWhiteBlackList = getParameterValue(request, "useWhiteBlackList");
        dbScheduleTask.setId(id);
        dbScheduleTask.setUseWhiteBlackList(useWhiteBlackList);
        dbScheduleTask.setTaskName(getParameterValue(request, "taskName"));
        dbScheduleTask.setPriority(Integer.parseInt(getParameterValue(request, "priority", "5")));
        dbScheduleTask.setPartitionMode(getParameterValue(request, "partitionMode", PartitionMode.STATIC.getCode()));
        dbScheduleTask.setDependentTaskId(getParameterValue(request, "dependentTaskId"));
        dbScheduleTask.setDependencePolicy(getParameterValue(request, "dependencePolicy", DependencePolicy.STRONG.getCode()));
        dbScheduleTask.setBlockPolicy(getParameterValue(request, "blockPolicy", BlockPolicy.COMMON.getCode()));
        dbScheduleTask.setAlarm(getParameterValue(request, "alarm", YesOrNo.YES.getCode()));
        dbScheduleTask.setTaskSwitch(getParameterValue(request, "taskSwitch", Switch.OPEN.getCode()));
        dbScheduleTask.setStatus(TaskStatus.NOT_INIT.getCode());
        dbScheduleTask.setVersion(1L);

        DistributedScheduleService.getInstance().insertDbScheduleTask(dbScheduleTask);
        // 校验任务是否使用黑白名单，如果有白名单则黑名单不生效
        if (YesOrNo.YES == YesOrNo.forCode(useWhiteBlackList)) {
            String whiteListStr = getParameterValue(request, "whiteList");
            if (whiteListStr != null) {
                String[] whiteListArray = whiteListStr.split(",");
                for (String whiteList : whiteListArray) {
                    String[] serverInfo = whiteList.split(":");
                    DbScheduleWhiteBlackList dbScheduleWhiteBlackList = new DbScheduleWhiteBlackList();
                    dbScheduleWhiteBlackList.setTaskId(id);
                    dbScheduleWhiteBlackList.setServerIp(serverInfo[0]);
                    dbScheduleWhiteBlackList.setServerPort(serverInfo[1]);
                    dbScheduleWhiteBlackList.setDirection(WhiteBlackList.WHITE.getCode());
                    DistributedScheduleService.getInstance().insertDbScheduleWhiteBlackList(dbScheduleWhiteBlackList);
                }
            } else {
                String blackListStr = getParameterValue(request, "blackList");
                if (blackListStr != null) {
                    String[] blackListArray = blackListStr.split(",");
                    for (String blackList : blackListArray) {
                        String[] serverInfo = blackList.split(":");
                        DbScheduleWhiteBlackList dbScheduleWhiteBlackList = new DbScheduleWhiteBlackList();
                        dbScheduleWhiteBlackList.setTaskId(id);
                        dbScheduleWhiteBlackList.setServerIp(serverInfo[0]);
                        dbScheduleWhiteBlackList.setServerPort(serverInfo[1]);
                        dbScheduleWhiteBlackList.setDirection(WhiteBlackList.BLACK.getCode());
                        DistributedScheduleService.getInstance().insertDbScheduleWhiteBlackList(dbScheduleWhiteBlackList);
                    }
                }
            }
        }
        // 唤醒任务调度者进行任务初始化操作
        DistributedScheduleManager.getSingleInstance().needInitMismatchTask();
        DistributedScheduleService.getInstance().signalAliveServerSchedule();
        return DataMsg.getSuccessDataMsg();
    }

    @DbSchedulePost("getAllTaskId")
    public DataMsg getAllTaskId() {
        DataMsg dataMsg = DataMsg.getSuccessDataMsg();
        dataMsg.setAttribute("list", DistributedScheduleService.getInstance().getAllTaskId());
        return dataMsg;
    }

    @DbSchedulePost("updateTask")
    public DataMsg updateTask(HttpServletRequest request) {
        DataMsg dataMsg = DataMsg.getFailDataMsg();
        DbScheduleTask dbScheduleTask = new DbScheduleTask();
        Map<String, Object> updateMap = new HashMap<>(8);

        String id = getParameterValue(request, "id");
        String type = getParameterValue(request, "type");
        String value = getParameterValue(request, "value");

        // 参数校验
        if (id == null || type == null) {
            return dataMsg;
        }

        boolean needSignal = false;
        if ("taskId".equalsIgnoreCase(type)) {
            if (value == null) {
                dataMsg.setMsgCode("taskIdNull");
                return dataMsg;
            }
            if (DistributedScheduleService.getInstance().checkExistByTaskId(value) > 0) {
                dataMsg.setMsgCode("taskIdExist");
                return dataMsg;
            }
            dbScheduleTask.setTaskId(value);
        } else if ("taskName".equalsIgnoreCase(type)) {
            if (value != null) {
                dbScheduleTask.setTaskName(value);
            } else {
                updateMap.put("deleteField", "task_name");
            }
        } else if ("beanId".equalsIgnoreCase(type) || "beanClass".equalsIgnoreCase(type)) {
            if (value != null) {
                dbScheduleTask.setTaskType(TaskType.NATIVE.getCode());
                if ("beanId".equalsIgnoreCase(type)) {
                    dbScheduleTask.setBeanId(value);
                } else {
                    dbScheduleTask.setBeanClass(value);
                }
            } else {
                DbScheduleTask currentDbScheduleTask = DistributedScheduleService.getInstance().getTaskDetailById(Long.parseLong(id));
                if ("beanId".equalsIgnoreCase(type)) {
                    if (currentDbScheduleTask.getBeanId() != null) {
                        if (currentDbScheduleTask.getBeanClass() != null) {
                            updateMap.put("deleteField", "bean_id");
                        } else {
                            dataMsg.setMsgCode("beanIsNull");
                            return dataMsg;
                        }
                    } else {
                        dataMsg = DataMsg.getSuccessDataMsg();
                        dataMsg.setMsgCode("valueAlreadyDelete");
                        return dataMsg;
                    }
                } else {
                    if (currentDbScheduleTask.getBeanClass() != null) {
                        if (currentDbScheduleTask.getBeanId() != null) {
                            updateMap.put("deleteField", "bean_class");
                        } else {
                            dataMsg.setMsgCode("beanIsNull");
                            return dataMsg;
                        }
                    } else {
                        dataMsg = DataMsg.getSuccessDataMsg();
                        dataMsg.setMsgCode("valueAlreadyDelete");
                        return dataMsg;
                    }
                }
            }
        } else if ("cronExpression".equalsIgnoreCase(type)) {
            if (value != null) {
                for (String cronExpressionStr : value.split(";")) {
                    if (!CronExpression.isValidExpression(cronExpressionStr.trim())) {
                        dataMsg.setMsgCode("cronExpressionError");
                        return dataMsg;
                    }
                }
                dbScheduleTask.setCronExpression(value);
                needSignal = true;
            } else {
                updateMap.put("deleteField", "cron_expression");
            }
        } else if ("nextFireTime".equalsIgnoreCase(type)) {
            if (value != null) {
                try {
                    Timestamp nextFireTime = Timestamp.valueOf(value);
                    if (nextFireTime.getTime() <= System.currentTimeMillis() + DistributedScheduleConstants.DEFAULT_IDLE_TIME_MILLI) {
                        needSignal = true;
                    }
                    dbScheduleTask.setNextFireTime(nextFireTime);
                } catch (Exception e) {
                    dataMsg.setMsgCode("nextFireTimeError");
                    return dataMsg;
                }
            } else {
                updateMap.put("deleteField", "next_fire_time");
            }
        } else {
            dataMsg.setMsgCode("updateTaskTypeError");
            return dataMsg;
        }

        if (updateMap.size() > 0) {
            updateMap.put("id", Long.parseLong(id));
            DistributedScheduleService.getInstance().updateTaskFieldValueToNullById(updateMap);
        } else {
            dbScheduleTask.setId(Long.parseLong(id));
            DistributedScheduleService.getInstance().updateDbScheduleTaskById(dbScheduleTask);
        }

        // 唤醒任务调度者进行任务初始化操作
        if (needSignal) {
            DistributedScheduleManager.getSingleInstance().needInitMismatchTask();
            DistributedScheduleService.getInstance().signalAliveServerSchedule();
        }
        return DataMsg.getSuccessDataMsg();
    }

    /**
     * 显示所有批量任务信息
     */
    @DbSchedulePost(value = "/getAllViewTask")
    public DataMsg getAllViewTask() {
        DataMsg dataMsg = DataMsg.getSuccessDataMsg();
        dataMsg.setAttribute("list", DistributedScheduleService.getInstance().getViewTask(null));
        return dataMsg;
    }

    /**
     * 根据分页信息显示对应的批量任务信息
     */
    @DbSchedulePost(value = "/getPagingViewTask")
    public DataMsg getPagingViewTask(HttpServletRequest request) {
        Map<String, Object> params = new HashMap<>(16);
        params.put("pageSize", Integer.parseInt(getParameterValue(request, "pageSize", "10")));
        params.put("currentPage", Integer.parseInt(getParameterValue(request, "currentPage", "1")));
        List<DbScheduleTaskView> pagingList = DistributedScheduleService.getInstance().getViewTask(params);
        DataMsg dataMsg = DataMsg.getSuccessDataMsg();
        dataMsg.setAttribute("pagingList", pagingList);
        return dataMsg;
    }

    /**
     * 根据主任务主键获取对应的主任务详细信息
     */
    @DbSchedulePost(value = "/getTaskDetailById/{id}")
    public DataMsg getTaskDetailById(@PathVariable("id") String id) {
        DataMsg dataMsg = DataMsg.getSuccessDataMsg();
        dataMsg.setAttribute("taskDetail", DistributedScheduleService.getInstance().getTaskDetailById(Long.parseLong(id)));
        return dataMsg;
    }

    /**
     * 根据主任务主键获取对应的子任务信息
     */
    @DbSchedulePost(value = "/getSubTaskByTaskId/{id}")
    public DataMsg getSubTaskByTaskId(@PathVariable("id") String id) {
        DataMsg dataMsg = DataMsg.getSuccessDataMsg();
        dataMsg.setAttribute("list", DistributedScheduleService.getInstance().getSubTaskByTaskId(Long.parseLong(id)));
        return dataMsg;
    }

    /**
     * 批量/单个任务开关修改
     */
    @DbSchedulePost(value = "/changeTaskSwitch/{ids}/{taskSwitch}")
    public DataMsg changeTaskSwitch(@PathVariable("ids") String ids, @PathVariable("taskSwitch") String taskSwitch) {
        Map<String, Object> updateMap = new HashMap<>(4);
        List<Long> idList = new ArrayList<>();
        for (String id : ids.split(",")) {
            idList.add(Long.parseLong(id));
        }
        updateMap.put("idList", idList);
        updateMap.put("taskSwitch", taskSwitch);
        DistributedScheduleService.getInstance().updateTaskSwitchById(updateMap);
        if (Switch.OPEN == Switch.forCode(taskSwitch)) {
            DistributedScheduleManager.getSingleInstance().needInitMismatchTask();
            DistributedScheduleService.getInstance().signalAliveServerSchedule();
        }
        return DataMsg.getSuccessDataMsg();
    }

    /**
     * 修改所有任务开关
     */
    @DbSchedulePost(value = "/changeAllTaskSwitch/{taskSwitch}")
    public DataMsg changeAllTaskSwitch(@PathVariable("taskSwitch") String taskSwitch) {
        Map<String, Object> updateMap = new HashMap<>(4);
        updateMap.put("taskSwitch", taskSwitch);
        DistributedScheduleService.getInstance().updateAllTaskSwitch(updateMap);
        if (Switch.OPEN == Switch.forCode(taskSwitch)) {
            DistributedScheduleManager.getSingleInstance().needInitMismatchTask();
            DistributedScheduleService.getInstance().signalAliveServerSchedule();
        }
        return DataMsg.getSuccessDataMsg();
    }

    /**
     * 删除任务
     */
    @DbSchedulePost(value = "/deleteTask/{ids}")
    public DataMsg deleteTask(@PathVariable("ids") String ids) {
        DataMsg returnData = DataMsg.getSuccessDataMsg();
        try {
            List<Long> idList = new ArrayList<>();
            for (String id : ids.split(",")) {
                idList.add(Long.parseLong(id));
            }
            DistributedScheduleService.getInstance().batchDeleteDbScheduleTaskById(idList);
            DistributedScheduleService.getInstance().batchDeleteDbScheduleSubTaskByParentId(idList);
        } catch (Exception e) {
            returnData.setFail();
            returnData.setMsgCode("deleteTaskFail");
            return returnData;
        }
        returnData.setMsgCode("deleteTaskSuccess");
        return returnData;
    }


    @Override
    public String getPath() {
        return "/dbschedule/html/task";
    }

}
